home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xmbase-grok-1.2 / dbfile.c < prev    next >
C/C++ Source or Header  |  1995-06-25  |  11KB  |  422 lines

  1. /*
  2.  * read and write database files.
  3.  *
  4.  *    write_dbase(form, path)        write dbase
  5.  *    read_dbase(form, path)        read dbase into empty dbase struct
  6.  */
  7.  
  8. #include "config.h"
  9. #include <X11/Xos.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <sys/stat.h>
  13. #include <assert.h>
  14. #include <signal.h>
  15. #ifdef DIRECT
  16. #include <sys/dir.h>
  17. #define  dirent direct
  18. #else
  19. #include <dirent.h>
  20. #endif
  21. #include <Xm/Xm.h>
  22. #include "grok.h"
  23. #include "form.h"
  24. #include "proto.h"
  25.  
  26. #define R_SEP    '\n'            /* row (card) separator */
  27. #define ESC    '\\'            /* treat next char literally */
  28.  
  29. extern int    errno;
  30. extern char    *progname;        /* argv[0] */
  31. extern Widget    toplevel;        /* top-level shell for icon name */
  32.  
  33.  
  34. /*
  35.  * If the print spooler dies for some reason, print a message. Don't let
  36.  * grok die because of the broken pipe signal.
  37.  */
  38.  
  39. static void broken_pipe_handler(int sig)
  40. {
  41.     create_error_popup(toplevel, 0,
  42.             "Procedural script aborted with signal %d\n", sig);
  43.     signal(SIGPIPE, SIG_IGN);
  44. }
  45.  
  46.  
  47. /*
  48.  * Write the database and all the items in it. The <path> is from the
  49.  * forms <dbase> field. Both the database and the form will be stored
  50.  * in a CARD structure; both are required to access cards. If the
  51.  * database is procedural, open a pipe to the database process and add
  52.  * -r option.
  53.  * Returns FALSE if the file could not be written.
  54.  */
  55.  
  56. static BOOL writefile(DBASE *, FORM *, int);
  57.  
  58. BOOL write_dbase(
  59.     DBASE            *dbase,        /* form and items to write */
  60.     FORM            *form,        /* contains column delimiter */
  61.     BOOL            force)        /* write even if not modified*/
  62. {
  63.     int            s;        /* section index */
  64.     BOOL            ok = TRUE;
  65.  
  66.     if (dbase->nsects == 1 && (dbase->modified || force))
  67.         ok = writefile(dbase, form, 0);
  68.     else
  69.         for (s=0; s < dbase->nsects; s++)
  70.             if (dbase->sect[s].modified &&
  71.                     (!dbase->sect[s].rdonly || force))
  72.                 ok &= writefile(dbase, form, s);
  73.     return(ok);
  74. }
  75.  
  76.  
  77. static BOOL writefile(
  78.     DBASE            *dbase,        /* form and items to write */
  79.     FORM            *form,        /* contains column delimiter */
  80.     int            nsect)        /* section to write */
  81. {
  82.     SECTION            *sect;        /* section to write */
  83.     char            *path;        /* file to write list to */
  84.     FILE            *fp;        /* open file */
  85.     int            r, c;        /* row and column counters */
  86.     int            hicol;        /* # of columns of card -1 */
  87.     char            *value;        /* converted data to write */
  88.     char            buf[40];    /* for date/time conversion */
  89.     register ITEM        *item;        /* item describing value */
  90.     register int        i;        /* index of item */
  91.     register char        *p;        /* string copy pointer */
  92.  
  93.     sect = &dbase->sect[nsect];
  94.     path = sect->path ? sect->path : form->dbase;
  95.     if (!path || !*path) {
  96.         create_error_popup(toplevel, 0,
  97.             "Database has no name, cannot save to disk");
  98.         return(FALSE);
  99.     }
  100.     path = resolve_tilde(path, 0);
  101.     if (form->proc) {
  102.         char cmd[1024];
  103.         sprintf(cmd, "%s -w %s", path, form->name);
  104.         if (!(fp = popen(cmd, "w"))) {
  105.             create_error_popup(toplevel, errno,
  106.                 "Failed to run shell script %s", cmd);
  107.             return(FALSE);
  108.         }
  109.     } else
  110.         if (!(fp = fopen(path, "w"))) {
  111.             create_error_popup(toplevel, errno,
  112.                 "Failed to create database file %s", path);
  113.             return(FALSE);
  114.         }
  115.     for (r=0; r < dbase->nrows; r++) {
  116.         if (nsect != dbase->row[r]->section)
  117.             continue;
  118.         for (hicol=dbase->row[r]->ncolumns-1; hicol > 0; hicol--)
  119.             if (dbase_get(dbase, r, hicol))
  120.                 break;
  121.         for (c=0; c <= hicol; c++) {
  122.             if (value = dbase_get(dbase, r, c)) {
  123.                 for (i=form->nitems-1; i >= 0; i--) {
  124.                     item = form->items[i];
  125.                     if (item->column == c)
  126.                         break;
  127.                 }
  128.                 if (i >= 0 && item->type == IT_TIME) {
  129.                     long secs = atoi(value);
  130.                     switch(item->timefmt) {
  131.                       case T_DATE:
  132.                         sprintf(value = buf, "%s",
  133.                             mkdatestring(secs));
  134.                         break;
  135.                       case T_TIME:
  136.                         sprintf(value = buf, "%s",
  137.                             mktimestring(secs, 0));
  138.                         break;
  139.                       case T_DATETIME:
  140.                         sprintf(value = buf, "%s %s",
  141.                             mkdatestring(secs),
  142.                             mktimestring(secs, 0));
  143.                         break;
  144.                       case T_DURATION:
  145.                         sprintf(value = buf, "%s",
  146.                             mktimestring(secs, 1));
  147.                         break;
  148.                     }
  149.                 }
  150.                 for (p=value; *p; p++) {
  151.                     if (*p == R_SEP ||
  152.                         *p == form->cdelim ||
  153.                         *p == ESC)
  154.                         fputc(ESC, fp);
  155.                     fputc(*p, fp);
  156.                 }
  157.             }
  158.             if (c < hicol)
  159.                 fputc(form->cdelim, fp);
  160.         }
  161.         fputc(R_SEP, fp);
  162.     }
  163.     if (form->proc)
  164.         if (pclose(fp)) {
  165.             create_error_popup(toplevel, errno,
  166.                 "%s:\nfailed to create database", path);
  167.             return(FALSE);
  168.         } else
  169.             return(TRUE);
  170.     else
  171.         fclose(fp);
  172.     if (sect)
  173.         sect->modified = FALSE;
  174.     dbase->modified = FALSE;
  175.     sect->mtime = time(0);
  176.     print_info_line();
  177.     return(TRUE);
  178. }
  179.  
  180.  
  181. /*
  182.  * Read a database from a file. The database must be empty (just created
  183.  * with dbase_create()). Returns FALSE if the file could not be read.
  184.  * Print a warning if the database is supposed to be writable but isn't.
  185.  *
  186.  * This works by first reading in fields, allocating one string for each.
  187.  * The strings are stored in a long list. Escaped separators are resolved.
  188.  * Row separators are stored as null pointers in the list. When the end
  189.  * of the file is reached, the number of rows and columns is known, and
  190.  * the pointers in the list are put in the right places of the dbase array.
  191.  */
  192.  
  193. #define LCHUNK    4096        /* alloc this many new list ptrs */
  194. #define BCHUNK    1024        /* alloc this many new chars for item */
  195.  
  196. static BOOL find_db_file     (DBASE *, FORM *, char *);
  197. static BOOL read_dir_or_file (DBASE *, FORM *, char *);
  198. static BOOL read_file         (DBASE *, FORM *, char *, time_t);
  199.  
  200. BOOL read_dbase(
  201.     DBASE            *dbase,        /* form and items to write */
  202.     FORM            *form,        /* contains column delimiter */
  203.     char            *path)        /* file to read list from */
  204. {
  205.     char            pathbuf[1024];    /* file name with path */
  206.     BOOL            ret;        /* return code, FALSE=error */
  207.  
  208.     if (!path || !*path) {
  209.         create_error_popup(toplevel, 0,
  210.             "Database has no name, cannot read from disk");
  211.         return(FALSE);
  212.     }
  213.     dbase_delete(dbase);
  214.     init_variables();
  215.     if (*path != '/' && *path != '~' && form->path) {
  216.         char *p;
  217.         strcpy(pathbuf, form->path);
  218.         if (p = strrchr(pathbuf, '/')) {
  219.             strcpy(p+1, path);
  220.             path = pathbuf;
  221.         }
  222.     }
  223.     if (!(ret = find_db_file(dbase, form, path)))
  224.         create_error_popup(toplevel, 0, "Failed to read %s", path);
  225.  
  226.     if (!dbase->nsects) {
  227.         if (!(dbase->sect = malloc(sizeof(SECTION)))) {
  228.             fprintf(stderr,
  229.                   "grok: no memory for section, cannot continue.");
  230.             exit(1);
  231.         }
  232.         mybzero(dbase->sect, sizeof(SECTION));
  233.         dbase->nsects = 1;
  234.     }
  235.     dbase->currsect = -1;
  236.     dbase->modified = FALSE;
  237.     print_info_line();
  238.     return(ret);
  239. }
  240.  
  241.  
  242. /*
  243.  * read both path.db and path, in that order. Return FALSE if both failed.
  244.  */
  245.  
  246. static BOOL find_db_file(
  247.     DBASE            *dbase,        /* form and items to write */
  248.     FORM            *form,        /* contains column delimiter */
  249.     char            *path)        /* file to read list from */
  250. {
  251.     char            pathbuf[1024];    /* file name with path */
  252.     int            nread = 0;    /* # of successful reads */
  253.  
  254.     sprintf(pathbuf, "%s.db", path);
  255.     if (!access(pathbuf, F_OK))
  256.         nread += read_dir_or_file(dbase, form, pathbuf);
  257.     if (!access(path, F_OK))
  258.         nread += read_dir_or_file(dbase, form, path);
  259.     return(nread > 0);
  260. }
  261.  
  262.  
  263. /*
  264.  * read path. If it's a directory, recurse. Return FALSE if nothing was found.
  265.  */
  266.  
  267. static BOOL read_dir_or_file(
  268.     DBASE            *dbase,        /* form and items to write */
  269.     FORM            *form,        /* contains column delimiter */
  270.     char            *path)        /* file to read list from */
  271. {
  272.     char            pathbuf[1024];    /* file name with path */
  273.     struct stat        statbuf;    /* is path a directory? */
  274.     DIR            *dir;        /* open directory file */
  275.     struct dirent        *dp;        /* one directory entry */
  276.     int            nfiles = 0;    /* # of files in directory */
  277.  
  278.     if (stat(path, &statbuf))
  279.         return(FALSE);
  280.     if (!(statbuf.st_mode & S_IFDIR))
  281.         return(read_file(dbase, form, path, statbuf.st_mtime));
  282.     if (!(dir = opendir(path)))
  283.         return(FALSE);
  284.     while (dp = readdir(dir))
  285.         if (*dp->d_name != '.') {
  286.             sprintf(pathbuf, "%s/%s", path, dp->d_name);
  287.             nfiles += read_dir_or_file(dbase, form, pathbuf);
  288.         }
  289.     (void)closedir(dir);
  290.     dbase->havesects = TRUE;
  291.     return(nfiles > 0);
  292. }
  293.  
  294.  
  295. /*
  296.  * read one plain file and append to dbase section list. Return FALSE on error.
  297.  */
  298.  
  299. static BOOL read_file(
  300.     DBASE            *dbase,        /* form and items to write */
  301.     FORM            *form,        /* contains column delimiter */
  302.     char            *path,        /* file to read list from */
  303.     time_t            mtime)        /* file modification time */
  304. {
  305.     SECTION            *sect;        /* new section */
  306.     FILE            *fp;        /* open file */
  307.     int            nc = 0;        /* size of curr line */
  308.     int            col = 0;    /* current column being added*/
  309.     int            row = -1;    /* current row being added */
  310.     register char        *buf, *p;    /* buffer for one item */
  311.     register int        bindex = 0;    /* next free byte in buf */
  312.     int            bsize;        /* size of buf in bytes */
  313.     char            c, c0;        /* next char from file */
  314.     BOOL            error;        /* in TRUE, abort */
  315.     ITEM            *item;        /* for time/date conversion */
  316.     int            i;
  317.  
  318.                             /* step 1: open file */
  319.     signal(SIGPIPE, broken_pipe_handler);
  320.     if (form->proc) {
  321.         char cmd[1024];
  322.         path = resolve_tilde(path, 0);
  323.         sprintf(cmd, "%s -r %s", path, form->name);
  324.         if (!(fp = popen(cmd, "r")))
  325.             return(FALSE);
  326.     } else {
  327.         path = resolve_tilde(path, "db");
  328.         if (!(fp = fopen(path, "r")))
  329.             return(FALSE);
  330.     }
  331.                             /* step 2: new sectn */
  332.     i = (dbase->nsects+1) * sizeof(SECTION);
  333.     if (!(sect = dbase->sect ? realloc(dbase->sect, i) : malloc(i))) {
  334.         create_error_popup(toplevel, errno,
  335.             "No memory for section %s", path);
  336.         form->proc ? pclose(fp) : fclose(fp);
  337.         return(FALSE);
  338.     }
  339.     dbase->sect = sect;
  340.     mybzero(sect = &dbase->sect[dbase->nsects], sizeof(SECTION));
  341.     dbase->currsect = dbase->nsects++;
  342.     sect->mtime = mtime;
  343.     sect->path  = mystrdup(path);
  344.                             /* step 3: read list */
  345.     buf = (char  *)malloc((bsize = BCHUNK) * sizeof(char));
  346.     error = !buf;
  347.     while (!error) {                    /* read file:*/
  348.         c = c0 = fgetc(fp);
  349.         if (!feof(fp) && c == ESC)
  350.             c = fgetc(fp);
  351.                                 /* end of str*/
  352.         if (feof(fp) || c0 == form->cdelim || c0 == R_SEP) {
  353.             if (!nc) {
  354.                 if (c0 == form->cdelim) col++;
  355.                 if (feof(fp))    break;
  356.                 else        continue;
  357.             }
  358.             buf[bindex] = 0;            /* next col */
  359.                                 /* .. date? */
  360.             for (i=-1, p=buf; *p; p++)
  361.                 if (*p=='.' || *p==':' || *p=='/') {
  362.                     for (i=form->nitems-1; i >= 0; i--) {
  363.                         item = form->items[i];
  364.                         if (item->column == col)
  365.                             break;
  366.                     }
  367.                     break;
  368.                 }
  369.             if (i >= 0 && item->type == IT_TIME)    /* .. -> int */
  370.                 switch(item->timefmt) {
  371.                   case T_DATE:
  372.                     sprintf(buf, "%d",
  373.                         parse_datestring(buf));
  374.                     break;
  375.                   case T_TIME:
  376.                     sprintf(buf, "%d",
  377.                         parse_timestring(buf, FALSE));
  378.                     break;
  379.                   case T_DATETIME:
  380.                     sprintf(buf, "%d",
  381.                         parse_datetimestring(buf));
  382.                     break;
  383.                   case T_DURATION:
  384.                     sprintf(buf, "%d",
  385.                         parse_timestring(buf, TRUE));
  386.                 }
  387.                                 /* store str */
  388.             if (row < 0)
  389.                 if (error |= !dbase_addrow(&row, dbase))
  390.                     break;
  391.             dbase_put(dbase, row, col++, buf);
  392.             bindex = 0;
  393.             if (feof(fp) || c0 == R_SEP) {        /* next row */
  394.                 col = nc = 0;
  395.                 row = -1;
  396.                 if (feof(fp))
  397.                     break;
  398.             }
  399.         } else {                    /* store char*/
  400.             if (bindex+1 >= bsize) {
  401.                 char *new = (char *)realloc(buf,
  402.                         (bsize += BCHUNK));
  403.                 if (error |= !new)
  404.                     break;
  405.                 buf = new;
  406.             }
  407.             buf[bindex++] = c;
  408.             nc++;
  409.         }
  410.     }
  411.                                 /* done. */
  412.     free(buf);
  413.     error |= form->proc ? !pclose(fp) : !!fclose(fp);
  414.     sect->modified = FALSE;
  415.     sect->rdonly   =
  416.     dbase->rdonly  = form->proc ? FALSE : !!access(path, W_OK);
  417.     if (error)
  418.         create_error_popup(toplevel, errno,
  419.             "Failed to allocate memory for\ndatabase %s", path);
  420.     return(!error);
  421. }
  422.